<template>
  <div :class="getClass" :style="getStyle" ref="wrapperRef">
    <PageHeader
      :ghost="ghost"
      :title="title"
      v-bind="omit($attrs, 'class')"
      :style="getHeaderStyle"
      ref="headerRef"
      v-if="getShowHeader"
    >
      <template #default>
        <template v-if="content">
          {{ content }}
        </template>
        <slot name="headerContent" v-else></slot>
      </template>
      <template #[item]="data" v-for="item in getHeaderSlots">
        <slot :name="item" v-bind="data || {}"></slot>
      </template>
    </PageHeader>

    <div class="overflow-hidden" :class="getContentClass" :style="getContentStyle" ref="contentRef">
      <slot></slot>
    </div>

    <PageFooter v-if="getShowFooter" ref="footerRef">
      <template #left>
        <slot name="leftFooter"></slot>
      </template>
      <template #right>
        <slot name="rightFooter"></slot>
      </template>
    </PageFooter>
  </div>
</template>
<script lang="ts" setup>
  import { PageWrapperFixedHeightKey } from '@/enums/pageEnum'
  import { useContentHeight } from '@/hooks/web/useContentHeight'
  import { useDesign } from '@/hooks/web/useDesign'
  import { propTypes } from '@/utils/propTypes'
  import { PageHeader } from 'ant-design-vue'
  import { omit, debounce } from 'lodash-es'
  import { useElementSize } from '@vueuse/core'
  import {
    CSSProperties,
    PropType,
    computed,
    provide,
    ref,
    unref,
    useAttrs,
    useSlots,
    watch,
  } from 'vue'
  import PageFooter from './PageFooter.vue'

  defineOptions({
    name: 'PageWrapper',
    inheritAttrs: false,
  })

  const props = defineProps({
    title: propTypes.string,
    dense: propTypes.bool,
    ghost: propTypes.bool,
    headerSticky: propTypes.bool,
    headerStyle: Object as PropType<CSSProperties>,
    content: propTypes.string,
    contentStyle: {
      type: Object as PropType<CSSProperties>,
    },
    contentBackground: propTypes.bool,
    contentFullHeight: propTypes.bool.def(false),
    contentClass: propTypes.string,
    fixedHeight: propTypes.bool,
    upwardSpace: propTypes.oneOfType([propTypes.number, propTypes.string]).def(0),
  })

  const attrs = useAttrs()
  const slots = useSlots()

  const wrapperRef = ref(null)
  const headerRef = ref(null)
  const contentRef = ref(null)
  const footerRef = ref(null)

  const { height } = useElementSize(wrapperRef)

  const { prefixCls } = useDesign('page-wrapper')

  provide(
    PageWrapperFixedHeightKey,
    computed(() => props.fixedHeight),
  )

  const getIsContentFullHeight = computed(() => {
    return props.contentFullHeight
  })

  const getUpwardSpace = computed(() => props.upwardSpace)
  const { redoHeight, setCompensation, contentHeight } = useContentHeight(
    getIsContentFullHeight,
    wrapperRef,
    [headerRef, footerRef],
    [contentRef],
    getUpwardSpace,
  )
  const debounceRedoHeight = debounce(redoHeight, 50)
  setCompensation({ useLayoutFooter: true, elements: [footerRef] })

  const getClass = computed(() => {
    return [
      prefixCls,
      {
        [`${prefixCls}--dense`]: props.dense,
      },
      attrs.class ?? {},
    ]
  })

  const getStyle = computed(() => {
    const { contentFullHeight, fixedHeight } = props
    return {
      ...(contentFullHeight && fixedHeight ? { height: '100%' } : {}),
    }
  })

  const getHeaderStyle = computed((): CSSProperties => {
    const { headerSticky } = props
    if (!headerSticky) {
      return {}
    }

    return {
      position: 'sticky',
      top: 0,
      zIndex: 99,
      ...props.headerStyle,
    }
  })

  const getShowHeader = computed(
    () => props.content || slots?.headerContent || props.title || getHeaderSlots.value.length,
  )

  const getShowFooter = computed(() => slots?.leftFooter || slots?.rightFooter)

  const getHeaderSlots = computed(() => {
    return Object.keys(omit(slots, 'default', 'leftFooter', 'rightFooter', 'headerContent'))
  })

  const getContentStyle = computed((): CSSProperties => {
    const { contentFullHeight, contentStyle, fixedHeight } = props
    if (!contentFullHeight) {
      return { ...contentStyle }
    }

    const height = `${unref(contentHeight)}px`
    return {
      ...contentStyle,
      minHeight: height,
      ...(fixedHeight ? { height } : {}),
    }
  })

  const getContentClass = computed(() => {
    const { contentBackground, contentClass } = props
    return [
      `${prefixCls}-content`,
      contentClass,
      {
        [`${prefixCls}-content-bg`]: contentBackground,
      },
    ]
  })

  watch(
    () => [getShowFooter.value],
    () => {
      redoHeight()
    },
    {
      flush: 'post',
      immediate: true,
    },
  )

  watch(height, () => {
    const { contentFullHeight, fixedHeight } = props
    contentFullHeight && fixedHeight && debounceRedoHeight()
  })
</script>
<style lang="less">
  @prefix-cls: ~'@{namespace}-page-wrapper';

  .@{prefix-cls} {
    position: relative;

    .@{prefix-cls}-content {
      margin: 16px;
    }

    .ant-page-header {
      &:empty {
        padding: 0;
      }
    }

    &-content-bg {
      background-color: @component-background;
    }

    &--dense {
      .@{prefix-cls}-content {
        margin: 0;
      }
    }
  }
</style>
